package edu.gatech.cs2340.mule.presenter;

import java.awt.Color;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.LinkedList;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.ColorUIResource;

import edu.gatech.cs2340.mule.model.LandOfficeInterface;
import edu.gatech.cs2340.mule.model.Map;
import edu.gatech.cs2340.mule.model.MapTile;
import edu.gatech.cs2340.mule.model.Mule;
import edu.gatech.cs2340.mule.model.MuleGame;
import edu.gatech.cs2340.mule.model.Player;
import edu.gatech.cs2340.mule.model.PubInterface;
import edu.gatech.cs2340.mule.model.RandomEvent;
import edu.gatech.cs2340.mule.model.StoreInterface;
import edu.gatech.cs2340.mule.model.TerrainTile;
import edu.gatech.cs2340.mule.model.Town;
import edu.gatech.cs2340.mule.model.Transaction;
import edu.gatech.cs2340.mule.model.TurnEngine;
import edu.gatech.cs2340.mule.view.MuleGameFrame;


public class MuleGamePresenter implements Serializable {
	
	private static final long serialVersionUID = 2431317303331550091L;
	
	private static final String SAVE_FILE_NAME = "mule.dat";
	public static final ColorUIResource UI_BACKGROUND_COLOR = new ColorUIResource(0,0,0);
	public static final ColorUIResource UI_FOREGROUND_COLOR = new ColorUIResource(253,208,23);
	public static final Color BACKGROUND_COLOR = new Color(0, 0, 0);
	public static final Color FOREGROUND_COLOR = new Color(253,208,23);
	//ctrl + f hex code ###### and replace with new hex code to change dialog box text
	
	
	private MuleGameFrame view;
	private MuleGame model;
	private Map model2;
	private TurnEngine turnEngine;
	
	//interfaces to utilize dependency injection and prevent circular dependency
	private LandOfficeInterface landOfficeInterface;
	private PubInterface pubInterface;
	private StoreInterface storeInterface;
	
	private Timer timer; //timer to coincide with TurnEngine's timer
	private boolean landGrantDialogBool;//boolean that designates whether or not
										//the players have been notified that
										//land grants are no longer available
	private UIManager UI;				//UIManager to color JOptionPanes
	

	/**
	 * Creates the Presenter by creating a new GUI and updating all the
	 * components on the GUI based on the data in the model.
	 * @param model
	 */
	public MuleGamePresenter(MuleGame model) {
		setUpPresenter(model);
		updateTimeLabel();		
		turnEngine.startInitialTurn();
		view.updateScoreboard(model.getPlayers());
		view.colorProductionPanel(model.getPlayers());
	}
	
	/**
	 * Fetches the imageSource for each mapTile and applies it to each
	 * button in the MapPanel.
	 */
	private void addImagesToButtons() {
		for(int i=0; i<model2.getMapWidth(); i++) {
			for(int j=0; j<model2.getMapHeight(); j++) {
				MapTile currentTile = model2.getMapTileAt(new Point(j,i));
				view.addImageToButton(i, j, currentTile.getImageSource());
			}
		}
	}
	
	/**
	 * Adds a listener that saves the game when executed.
	 */
	private void addSaveListener() {
		view.addSaveListener(new ActionListener() {
			public void actionPerformed(ActionEvent ae) {
				saveGame();
			}
		});
	}
	
	/**
	 * Adds a listener that executes whenever a player passes.
	 */
	private void addPassListener() {
		view.addPassListener(new ActionListener() {
			public void actionPerformed(ActionEvent ae) {
				//passes the turn to the next player. note the pass button
				//is only available during land selection
				
				turnEngine.addPassPerRound();
				
				if(!model.checkInLandSelectionPhase()) {
					//counts as last pass of land selection in a round.
					//move into regular gameplay and remove the pass button
					turnEngine.stopTimer();
					view.updateBottomBannerPanel("<html><font color=#fdd017>All Players have passed. " +
							"<html><font color=#fdd017>It is the end of Land Selection phase. Normal gameplay will now begin.");
					view.removePass();
					view.disableMap();
					LinkedList<Point> l = new LinkedList<Point>();
					l.add(model.getMap().findTown());
					view.enableButtons(l);
					view.enterTown();
					model.setInLandGrantPhase(false);
					
					turnEngine.setupNextLandSelectionTurn();
					String nextTurnMessage = "<html><font color=#fdd017>It is now " +
							turnEngine.getCurrentPlayerName() + "<html><font color=#fdd017>'s turn. Click OK to start your turn.";
					JOptionPane.showMessageDialog(view, nextTurnMessage);					
					turnEngine.startTurn();
					turnEngine.resetGameRound();
					updatePlayerInfoPanel();
					view.addRoundLabel();					
				}
				else {
					//regular pass (not the last pass). transfer turn to next
					//player to continue land selection
					turnEngine.stopTimer();
					String nextTurnMessage = "<html><font color=#fdd017>It is the end of " + turnEngine.getCurrentPlayerName();
					turnEngine.setupNextLandSelectionTurn();
					if(!model.isInLandGrantPhase() && model.checkInLandSelectionPhase() && !landGrantDialogBool) {
						view.updateBottomBannerPanel("<html><font color=#fdd017>Land Grant phase has ended. Properties are no longer free.");
						landGrantDialogBool = true;
					}
					nextTurnMessage += "<html><font color=#fdd017>'s turn. It is now " +
							turnEngine.getCurrentPlayerName() + "<html><font color=#fdd017>'s turn. Click OK to start your turn.";
					JOptionPane.showMessageDialog(view, nextTurnMessage);
					turnEngine.startTurn();
					updatePlayerInfoPanel();
				}
				
			}
		});
	}
	
	/**
	 * Adds the specific listeners to each button in the town.
	 */
	private void addTownListeners() {
		view.addExitListener(new ActionListener() {
			public void actionPerformed(ActionEvent ae) {
				//move to map panel
				view.exitTown();
			}
		});
		view.addStoreListener(new ActionListener() {
			public void actionPerformed(ActionEvent ae) {
				//move to store panel
				view.enterStore();
			}
		});
		view.addPubListener(new ActionListener() {
			public void actionPerformed(ActionEvent ae) {
				//clicked on pub
				int bonus = Transaction.gambleAtPub(pubInterface, turnEngine.getCurrentPlayer());
				turnEngine.stopTimer();
				String nextTurnMessage = "<html><font color=#fdd017>" +
						turnEngine.getCurrentPlayerName() + " won " + bonus + " credits!";
				view.updateBottomBannerPanel(nextTurnMessage);
				turnEngine.setupNextTurn();
				if(turnEngine.checkIfNewRound()) {
					performNewRoundLogic();
					//perform random round action
				}				
				nextTurnMessage = "<html><font color=#fdd017>It is now " + turnEngine.getCurrentPlayerName() + "'s turn. Click OK to start your turn.";
				JOptionPane.showMessageDialog(view, nextTurnMessage);
				
				turnEngine.startTurn();
				updatePlayerInfoPanel();
			}
		});
		view.addLandOfficeListener(new ActionListener() {
			public void actionPerformed(ActionEvent ae) {
				//clicked on land office
				String enterLandOfficeMessage = "<html><font color=#fdd017>Entering Land Office. Enter Town to Cancel.";
				view.updateBottomBannerPanel(enterLandOfficeMessage);
				model.setIsBuyingLand(true);
				view.exitTown();
				view.enableMap();
			}
		});
	}
	
	/**
	 * Adds the specific listeners to each button in the store.
	 */
	private void addStoreListeners() {
		
		view.addBuyListener(new ActionListener() {
			public void actionPerformed(ActionEvent ae) {
				//update price label and spinner cost based on current
				//game state, then move the player to the buy panel
				String resource = view.getSelectedResourceToBuy();
				int amount = view.getSelectedAmountToBuy();
				if(resource.equals("Food")) {
					//update spinner max based on stock
					view.updateBuyPanelSpinnerMax(
							storeInterface.getFoodStock());
					view.updateBuyPanelPriceLabel(
							storeInterface.calculateFoodPurchasePrice(amount));
				}
				else if(resource.equals("Energy")) {
					//update spinner max based on stock
					view.updateBuyPanelSpinnerMax(
							storeInterface.getEnergyStock());
					view.updateBuyPanelPriceLabel(
							storeInterface.calculateEnergyPurchasePrice(amount));
				}
				else if(resource.equals("Smithore")) {
					//update spinner max based on stock
					view.updateBuyPanelSpinnerMax(
							storeInterface.getSmithoreStock());
					view.updateBuyPanelPriceLabel(
							storeInterface.calculateSmithorePurchasePrice(amount));
				}
				view.enterBuyPanel();
			}
		});
		view.addSellListener(new ActionListener() {
			public void actionPerformed(ActionEvent ae) {
				//update price label and spinner cost based on current
				//game state, then move the player to the sell panel
				String resource = view.getSelectedResourceToSell();
				int amount = view.getSelectedAmountToSell();
				if(resource.equals("Food")) {
					//update spinner max based on stock
					view.updateSellPanelSpinnerMax(
							turnEngine.getCurrentPlayer().getFoodAmount());
					view.updateSellPanelPriceLabel(
							storeInterface.calculateFoodPurchasePrice(amount));
				}
				else if(resource.equals("Energy")) {
					//update spinner max based on stock
					view.updateSellPanelSpinnerMax(
							turnEngine.getCurrentPlayer().getEnergyAmount());
					view.updateSellPanelPriceLabel(
							storeInterface.calculateEnergyPurchasePrice(amount));
				}
				else if(resource.equals("Smithore")) {
					//update spinner max based on stock
					view.updateSellPanelSpinnerMax(
							turnEngine.getCurrentPlayer().getSmithOreAmount());
					view.updateSellPanelPriceLabel(
							storeInterface.calculateSmithorePurchasePrice(amount));
				}
				view.enterSellPanel();
			}
		});
		view.addStoreExitListener(new ActionListener() {
			public void actionPerformed(ActionEvent ae) {
				//move back to the town panel
				view.exitStore();
			}
		});
		view.addBuyMuleListener(new ActionListener() {
			public void actionPerformed(ActionEvent ae) {
				//update the price label in mule buying panel, and then
				//move the player to that panel
				int muleString = view.getSelectedMuleType();
				String muleType;				
				switch (muleString) {
					case 0:
						muleType = Mule.FOOD_TYPE;
						break;

					case 1:
						muleType = Mule.ENERGY_TYPE;
						break;
						
					default:
						muleType = Mule.SMITHORE_TYPE;
						break;
				}
				int amount = storeInterface.calculateMulePurchasePrice(muleType);
				view.updateMulePriceLabel(amount);
				view.enterMuleBuyingPanel();
			}
		});
		view.addBuyPanelBuyListener(new ActionListener() {
			public void actionPerformed(ActionEvent ae) {
				//buy stuff
				String resource = view.getSelectedResourceToBuy();
				int amount = view.getSelectedAmountToBuy();
				if(resource.equals("Food")) {
					//buy food
					if(Transaction.buyFoodFromStore(storeInterface,
							turnEngine.getCurrentPlayer(), amount)) {
						view.updateBuyPanelSpinnerMax(
							storeInterface.getFoodStock());
						view.updateBuyPanelPriceLabel(0);
					}
					else {
						view.recolorPriceLabel();
					}
					
				}
				else if(resource.equals("Energy")) {
					//buy energy
					if(Transaction.buyEnergyFromStore(storeInterface,
							turnEngine.getCurrentPlayer(), amount)) {
						view.updateBuyPanelSpinnerMax(
							storeInterface.getEnergyStock());
						view.updateBuyPanelPriceLabel(0);
					}
					else {
						view.recolorPriceLabel();
					}
					
				}
				else if(resource.equals("Smithore")) {
					//buy smithore
					if(Transaction.buySmithoreFromStore(storeInterface,
							turnEngine.getCurrentPlayer(), amount)) {
						view.updateBuyPanelSpinnerMax(
							storeInterface.getSmithoreStock());
						view.updateBuyPanelPriceLabel(0);
					}
					else {
						view.recolorPriceLabel();
					}
				}
				updatePlayerInfoPanel();
			}
		});
		view.addBuyPanelCancelListener(new ActionListener() {
			public void actionPerformed(ActionEvent ae) {
				//move back to the store panel
				view.moveFromBuyToStore();
			}
		});
		view.addBuyPanelSpinnerListener(new ChangeListener() {
			public void stateChanged(ChangeEvent ae) {
				String resource = view.getSelectedResourceToBuy();
				int amount = view.getSelectedAmountToBuy();
				if(resource.equals("Food")) {
					//update price
					view.updateBuyPanelPriceLabel(
							storeInterface.calculateFoodPurchasePrice(amount));
				}
				else if(resource.equals("Energy")) {
					//update price
					view.updateBuyPanelPriceLabel(
							storeInterface.calculateEnergyPurchasePrice(amount));
				}
				else if(resource.equals("Smithore")) {
					//update price
					view.updateBuyPanelPriceLabel(
							storeInterface.calculateSmithorePurchasePrice(amount));
				}
			}
		});
		view.addBuyPanelComboListener(new ItemListener() {
			public void itemStateChanged(ItemEvent ie) {
				String resource = view.getSelectedResourceToBuy();
				int amount = view.getSelectedAmountToBuy();
				if(resource.equals("Food")) {
					//update spinner max based on stock
					view.updateBuyPanelSpinnerMax(
							storeInterface.getFoodStock());
					view.updateBuyPanelPriceLabel(
							storeInterface.calculateFoodPurchasePrice(amount));
				}
				else if(resource.equals("Energy")) {
					//update spinner max based on stock
					view.updateBuyPanelSpinnerMax(
							storeInterface.getEnergyStock());
					view.updateBuyPanelPriceLabel(
							storeInterface.calculateEnergyPurchasePrice(amount));
				}
				else if(resource.equals("Smithore")) {
					//update spinner max based on stock
					view.updateBuyPanelSpinnerMax(
							storeInterface.getSmithoreStock());
					view.updateBuyPanelPriceLabel(
							storeInterface.calculateSmithorePurchasePrice(amount));
				}
			}

		});
		view.addSellPanelSpinnerListener(new ChangeListener() {
			public void stateChanged(ChangeEvent ae) {
				String resource = view.getSelectedResourceToSell();
				int amount = view.getSelectedAmountToSell();			       
				if(resource.equals("Food")) {
					//update price
					view.updateSellPanelPriceLabel(
							storeInterface.calculateFoodPurchasePrice(amount));
				}
				else if(resource.equals("Energy")) {
					//update price
					view.updateSellPanelPriceLabel(
							storeInterface.calculateEnergyPurchasePrice(amount));
				}
				else if(resource.equals("Smithore")) {
					//update price
					view.updateSellPanelPriceLabel(
							storeInterface.calculateSmithorePurchasePrice(amount));
				}
			}
		});
		view.addSellPanelComboListener(new ItemListener() {
			public void itemStateChanged(ItemEvent ie) {
				String resource = view.getSelectedResourceToSell();
				int amount = view.getSelectedAmountToSell();
				if(resource.equals("Food")) {
					//update spinner max based on stock
					view.updateSellPanelSpinnerMax(
							turnEngine.getCurrentPlayer().getFoodAmount());
					view.updateSellPanelPriceLabel(
							storeInterface.calculateFoodPurchasePrice(amount));
				}
				else if(resource.equals("Energy")) {
					//update spinner max based on stock
					view.updateSellPanelSpinnerMax(
							turnEngine.getCurrentPlayer().getEnergyAmount());
					view.updateSellPanelPriceLabel(
							storeInterface.calculateEnergyPurchasePrice(amount));
				}
				else if(resource.equals("Smithore")) {
					//update spinner max based on stock
					view.updateSellPanelSpinnerMax(
							turnEngine.getCurrentPlayer().getSmithOreAmount());
					view.updateSellPanelPriceLabel(
							storeInterface.calculateSmithorePurchasePrice(amount));
				}
			}

		});
		view.addSellPanelSellListener(new ActionListener() {
			public void actionPerformed(ActionEvent ae) {
				//sell stuff
				String resource = view.getSelectedResourceToSell();
				int amount = view.getSelectedAmountToSell();
				if(resource.equals("Food")) {
					//sell food
					Transaction.sellFoodToStore(storeInterface,
							turnEngine.getCurrentPlayer(), amount);
					view.updateSellPanelSpinnerMax(
							turnEngine.getCurrentPlayer().getFoodAmount());
					view.updateSellPanelPriceLabel(0);
				}
				else if(resource.equals("Energy")) {
					//sell energy
					Transaction.sellEnergyToStore(storeInterface,
							turnEngine.getCurrentPlayer(), amount);
					view.updateSellPanelSpinnerMax(
							turnEngine.getCurrentPlayer().getEnergyAmount());
					view.updateSellPanelPriceLabel(0);
				}
				else if(resource.equals("Smithore")) {
					//sell smithore
					Transaction.sellSmithoreToStore(storeInterface,
							turnEngine.getCurrentPlayer(), amount);
					view.updateSellPanelSpinnerMax(
							turnEngine.getCurrentPlayer().getSmithOreAmount());
					view.updateSellPanelPriceLabel(0);
				}
				updatePlayerInfoPanel();
			}
		});
		view.addSellPanelCancelListener(new ActionListener() {
			public void actionPerformed(ActionEvent ae) {
				//move back to the store
				view.moveFromSellToStore();
			}
		});
		view.addMulePanelBuyListener(new ActionListener() {
			public void actionPerformed(ActionEvent ae) {
				//move to the map and allow the player to click on tiles
				//to place a mule
				int muleString = view.getSelectedMuleType();
				String muleType;
				Mule mule;
				
				switch (muleString) {
					case 0:
						muleType = Mule.FOOD_TYPE;
						break;

					case 1:
						muleType = Mule.ENERGY_TYPE;
						break;
						
					default:
						muleType = Mule.SMITHORE_TYPE;
						break;
				}
				
				mule = Transaction.buyMuleFromStore(storeInterface,
						turnEngine.getCurrentPlayer(), muleType);
				if(mule != null) {
					model.setUnplacedMule(mule);
					view.moveFromMuleBuyingToMap();
					view.enableMap();
					model.setIsPlacingMule(true);
				}
				else {
					view.setMulePriceLabelColor();
				}
			}
		});
		view.addMulePanelCancelListener(new ActionListener() {
			public void actionPerformed(ActionEvent ae) {
				//swap back to the store panel
				view.moveFromMuleToStore();
			}
		});
		view.addMulePanelComboListener(new ItemListener() {
			public void itemStateChanged(ItemEvent ie) {
				//update the price label in the mule buying panel when the user
				//selects a mule type to buy
				int muleString = view.getSelectedMuleType();
				String muleType;				
				switch (muleString) {
					case 0:
						muleType = Mule.FOOD_TYPE;
						break;

					case 1:
						muleType = Mule.ENERGY_TYPE;
						break;
						
					default:
						muleType = Mule.SMITHORE_TYPE;
						break;
				}
				int amount = storeInterface.calculateMulePurchasePrice(muleType);
				view.updateMulePriceLabel(amount);
			}
		});
		
	}
	
	/**
	 * Updates the time label to display the current time left in the player's turn
	 * @param time
	 */
	private void updateTimeLabel() {
		timer = new Timer(250, new ActionListener() {
		
			@Override
			public void actionPerformed(ActionEvent e) {
				view.updateTimeLabel(turnEngine.getTimeLeft());
				if(turnEngine.getTimeLeft()==0) {
					String nextTurnMessage = "<html><font color=#fdd017>Time's up! It is the end of " + turnEngine.getCurrentPlayerName();
					if(turnEngine.checkIfNewRound() && model.checkInLandSelectionPhase()) {
						turnEngine.setupNextLandSelectionTurn();
					}
					else {
						turnEngine.setupNextTurn();
					}					
					if(turnEngine.checkIfNewRound() && !model.checkInLandSelectionPhase()) {
						performNewRoundLogic();
						//perform random round action
					}
					if(!model.isInLandGrantPhase() && model.checkInLandSelectionPhase() && !landGrantDialogBool) {
						view.updateBottomBannerPanel("<html><font color=#fdd017>Land Grant phase has ended. Properties are no longer free.");
						landGrantDialogBool = true;
					}
					nextTurnMessage += "<html><font color=#fdd017>'s turn. It is now " + turnEngine.getCurrentPlayerName() + "'s turn. Click OK to start your turn.";
					JOptionPane.showMessageDialog(view, nextTurnMessage);
					turnEngine.startTurn();
					updatePlayerInfoPanel();
					view.moveToTown();
				}
			}
		});
		timer.start();
		timer.setRepeats(true);
	}
	
	/**
	 * Adds the listener for each button in the grid. Contains the logic
	 * for what happens when a button is pressed.
	 */
	private void addListenersToGrid() {
		view.addListenersToGrid(new ActionListener() {
			public void actionPerformed(ActionEvent ae) {
				JButton pressedButton = (JButton) ae.getSource();
				Point tilePoint = view.getButtonPoint(pressedButton);
				
				//if the button was the town, but canceling buying land from office
				if(model2.getMapTileAt(tilePoint) instanceof Town
						&& !model.isPlacingMule() && model.isBuyingLand()) {
					view.enterTown();
					String failMessage = "<html><font color=#fdd017>Purchase Cancelled";
					view.updateBottomBannerPanel(failMessage);
					view.disableMap();
					LinkedList<Point> l = new LinkedList<Point>();
					l.add(model.getMap().findTown());
					view.enableButtons(l);
					model.setIsBuyingLand(false);
				}
				else if(model2.getMapTileAt(tilePoint) instanceof Town
						&& !model.isPlacingMule() && !model.isBuyingLand()) {
					view.enterTown();
				}
				else if(model.isPlacingMule()
						&& !(model2.getMapTileAt(tilePoint) instanceof Town)) {
					//place mule
					Mule mule = model.getUnplacedMule();
					Player activePlayer = turnEngine.getCurrentPlayer();
					if(activePlayer.isOwned(tilePoint) && !model2.getMapTileAt(tilePoint).hasMule()) {
						Map map = model.getMap();
						TerrainTile terrainTile = (TerrainTile) map.getMapTileAt(tilePoint);
						terrainTile.addMule(mule);
						view.updateBottomBannerPanel("<html><font color=#fdd017>Mule placed successfully.");
					}
					else {
						view.updateBottomBannerPanel("<html><font color=#fdd017>Failed to place Mule. " +
								"You do not own that property or you already have a mule on that property.");
					}
					model.setUnplacedMule(null);
					model.setIsPlacingMule(false);
					view.disableMap();
					LinkedList<Point> l = new LinkedList<Point>();
					l.add(model.getMap().findTown());
					view.enableButtons(l);
					view.moveFromMapToMuleBuying();
					updatePlayerInfoPanel();
				}
				else {
					//buy/sell property
					if(model.isInLandGrantPhase() && model.checkInLandSelectionPhase()) { //free land at beginning of game
						if(Transaction.performLandGrant(landOfficeInterface,
							turnEngine.getCurrentPlayer(), tilePoint)) {
							
								//update button color
								view.updateButtonColor(tilePoint, turnEngine.getCurrentPlayer());
								
								turnEngine.stopTimer();
								String nextTurnMessage = "<html><font color=#fdd017>It is the end of " + turnEngine.getCurrentPlayerName() +"'s turn.";
								JOptionPane.showMessageDialog(view, nextTurnMessage);
								turnEngine.setupNextLandSelectionTurn();
								if(!model.isInLandGrantPhase()) {
									view.updateBottomBannerPanel("<html><font color=#fdd017>Land Grant phase has ended. Properties are no longer free.");
									landGrantDialogBool = true;
								}
								nextTurnMessage = "<html><font color=#fdd017>It is now " + turnEngine.getCurrentPlayerName() + "'s turn. Click OK to start your turn.";
								JOptionPane.showMessageDialog(view, nextTurnMessage);
								turnEngine.startTurn();
								updatePlayerInfoPanel();
								
							
						}
					}
					else if(model.checkInLandSelectionPhase()) { // purchased land selection at beginning of game
						if(Transaction.buyFromLandOffice(landOfficeInterface, turnEngine.getCurrentPlayer(), tilePoint)) {
							view.updateButtonColor(tilePoint, turnEngine.getCurrentPlayer());
							updatePlayerInfoPanel();
							turnEngine.stopTimer();
							String nextTurnMessage = "<html><font color=#fdd017>It is the end of " + turnEngine.getCurrentPlayerName();
							turnEngine.setupNextLandSelectionTurn();
							nextTurnMessage += "<html><font color=#fdd017>'s turn. It is now " + turnEngine.getCurrentPlayerName() + "'s turn. Click OK to start your turn.";
							JOptionPane.showMessageDialog(view, nextTurnMessage);
							turnEngine.startTurn();
							updatePlayerInfoPanel();
						}
					}
					else { //buy/sell during regular game
						if(Transaction.buyFromLandOffice(landOfficeInterface, turnEngine.getCurrentPlayer(), tilePoint)) {
							view.updateButtonColor(tilePoint, turnEngine.getCurrentPlayer());
							String successMessage = "<html><font color=#fdd017>Purchase Successful";
							view.updateBottomBannerPanel(successMessage);
							updatePlayerInfoPanel();
							view.disableMap();
							LinkedList<Point> l = new LinkedList<Point>();
							l.add(model.getMap().findTown());
							view.enableButtons(l);
							view.enterTown();
							model.setIsBuyingLand(false);
						}
						else {
							String failMessage = "<html><font color=#fdd017>Purchase Unsuccessful";
							view.updateBottomBannerPanel(failMessage);
							view.disableMap();
							LinkedList<Point> l = new LinkedList<Point>();
							l.add(model.getMap().findTown());
							view.enableButtons(l);
							view.enterTown();
							model.setIsBuyingLand(false);
						}
					}
					
					
					
				}
			}
		});
	}
	
	/**
	 * Pass model's information about the current player to the GUI for display
	 */
	private void updatePlayerInfoPanel() {
		if(model.checkInLandSelectionPhase()) {
			view.updatePlayerInfoPanelDuringLandSelection(
					turnEngine.getCurrentPlayer(), turnEngine.getGameRound());
		}
		else {
			view.updatePlayerInfoPanel(turnEngine.getCurrentPlayer(), turnEngine.getGameRound());
		}
		
	}
	
	/**
	 * Sets up all the listeners for each component. Useful to readd listeners
	 * when a saved game is loaded since they are transient.
	 */
	public void restoreGame() {
		UI.put("OptionPane.background",UI_BACKGROUND_COLOR);
		UI.put("Panel.background",UI_BACKGROUND_COLOR);
		UI.put("OptionPane.foreground",UI_FOREGROUND_COLOR);
		UI.put("Panel.foreground",UI_FOREGROUND_COLOR);
		
		updatePlayerInfoPanel();
		addImagesToButtons();
		addListenersToGrid();
		addTownListeners();
		addStoreListeners();
		addPassListener();
		addSaveListener();
		updateTimeLabel();		
		turnEngine.startTurn();
		view.setVisible(true);
	}
	
	/**
	 * Saves the game to the specified .dat file
	 */
	private void saveGame() {
		try{
			Files.deleteIfExists(Paths.get(SAVE_FILE_NAME));
			ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(SAVE_FILE_NAME));
			out.writeObject(this);
		}
		catch (FileNotFoundException e) {
			System.out.println("File not found");
		}
		catch (IOException e) {
			System.out.println("General IO Error");
		}
		JOptionPane.showMessageDialog(view, "<html><font color=#fdd017>Game Saved");
	}
	
	/**
	 * Creates each part of the presenter.
	 * @param model
	 */
	private void setUpPresenter(MuleGame model) {
		UI = new UIManager();
		UI.put("OptionPane.background",UI_BACKGROUND_COLOR);
		UI.put("Panel.background",UI_BACKGROUND_COLOR);
		UI.put("OptionPane.foreground",UI_FOREGROUND_COLOR);
		UI.put("Panel.foreground",UI_FOREGROUND_COLOR);
		
		this.model = model;
		model2 = model.getMap();
		view = new MuleGameFrame();
		
		//disable the town until land selection is over
		LinkedList<Point> l = new LinkedList<Point>();
		l.add(model.getMap().findTown());
		view.disableButtons(l);
		
		view.setSize(1200,700);
		view.setVisible(true);
		view.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		landOfficeInterface = model.createLandOfficeInterface();
		pubInterface = model.createPubInterface();
		storeInterface = model.createStoreInterface();
		turnEngine = model.getTurnEngine();
		landGrantDialogBool = false;
		
		updatePlayerInfoPanel();
		addImagesToButtons();
		addListenersToGrid();
		addTownListeners();
		addStoreListeners();
		addPassListener();
		addSaveListener();
	}
	
	/**
	 * Takes care of new round operations (production and random events). Displays
	 * to the players changes to the game state as they happen during this process.
	 */
	private void performNewRoundLogic() {
		JOptionPane.showMessageDialog(view, "<html><font color=#fdd017>End of Round " + (turnEngine.getGameRound()-1));
		//calculate production
		for(int i=0; i<model.getPlayers().length; i++) {
			int[] prod = model.getPlayers()[i].calculateProduction(model2);
			view.updateProductionPanel(prod, i);
		}
		//random events
		String randString = "";
		for(Player p : model.getPlayers()) {
			RandomEvent randomEvent = new RandomEvent(p, turnEngine.getGameRound());
			if(randomEvent.eventOccurred()) {
				randString += randomEvent.getMessage() + "<html><br>";
			}
		}
		if(randString.length()>0) {
			view.updateBottomBannerPanel(randString);
		}
		
		
		turnEngine.calculatePlayerOrder();
		view.updateScoreboard(turnEngine.getPlayersInScoreOrder());
	}
}
